package top.wilsonlv.jaguar.oauth2.resourceserver.config;

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import top.wilsonlv.jaguar.commons.web.WebConstant;
import top.wilsonlv.jaguar.oauth2.resourceserver.properties.JaguarResourceServerProperties;
import top.wilsonlv.jaguar.security.component.AuthenticationExceptionHandler;
import top.wilsonlv.jaguar.security.component.JaguarAccessDeniedHandler;

import java.util.Map;

import static top.wilsonlv.jaguar.commons.web.WebConstant.STATIC_RESOURCE_EXTENSIONS;

/**
 * @author lvws
 * @since 2021/4/8
 */
@Configuration
@EnableResourceServer
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {

    private final JaguarResourceServerProperties jaguarResourceServerProperties;

    private final JaguarAccessDeniedHandler jaguarAccessDeniedHandler;

    private final AuthenticationExceptionHandler authenticationExceptionHandler;

    private final RedisConnectionFactory redisConnectionFactory;

    @Value("${spring.application.name}")
    private String applicationName;

    public String getAllScopeName() {
        return applicationName + "所有权限";
    }

    public String getOtherScopeName() {
        return applicationName + "其余权限";
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
        redisTokenStore.setPrefix("oauth2:");

        resources.resourceId(applicationName)
                .tokenStore(redisTokenStore)
                .accessDeniedHandler(jaguarAccessDeniedHandler)
                .authenticationEntryPoint(authenticationExceptionHandler);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                //不需要认证就可以访问的
                .authorizeRequests();

        for (String extension : STATIC_RESOURCE_EXTENSIONS) {
            registry.antMatchers(WebConstant.MATCH_ALL + extension).permitAll();
        }

        if (jaguarResourceServerProperties.getIgnoreUrls() != null) {
            for (String ignoreUrl : jaguarResourceServerProperties.getIgnoreUrls()) {
                registry.antMatchers(ignoreUrl).permitAll();
            }
        }

        if (jaguarResourceServerProperties.getScopeUrls() != null) {
            for (Map.Entry<String, String> entry : jaguarResourceServerProperties.getScopeUrls().entrySet()) {
                String accessRule = String.format("#oauth2.hasAnyScope('%s', '%s')", entry.getValue(), getAllScopeName());
                registry.antMatchers(entry.getKey()).access(accessRule);
            }
        }

        String anyRequestAccessRule = String.format("#oauth2.hasAnyScope('%s', '%s')", getOtherScopeName(), getAllScopeName());
        registry.anyRequest().access(anyRequestAccessRule)
                .and().exceptionHandling()
                .accessDeniedHandler(jaguarAccessDeniedHandler)
                .authenticationEntryPoint(authenticationExceptionHandler)
                //异常处理
                .and().cors()
                .and().csrf().disable()
                .headers().frameOptions().disable();
    }

}
